HTTP Client 與瀏覽器指紋:TLS ClientHello、JA3/JA4、HTTP/2 與 HTTP/3

將瀏覽器中的 API 呼叫搬到 Python 或 Rust 程式時,常見的做法是參考開發者工具中的 Request,複製 URL、HTTP Method、Header 與 Cookie。這些資訊描述了應用程式要送出什麼請求;即使內容一致,伺服器仍可能從連線中觀察到不同的 Client 特徵。

差異來自請求背後的連線過程。瀏覽器除了組合 HTTP 請求,還要透過底層網路元件建立連線、協商加密方式與 HTTP 版本。程式採用的網路元件也有自己的預設參數與處理順序;複製瀏覽器的 Header,不會同步套用瀏覽器底層的連線設定。

伺服器可以將這些由協定實作留下的特徵整理、比對,用來辨識相近的 Client 類型或發現異常組合,這就是 HTTP Client Fingerprinting。要理解這些特徵的來源,需要把應用程式設定的請求內容,和底層實際建立的連線分開觀察。

HTTP Client 指紋的分層模型

以一筆讀取帳號資料的 HTTPS 請求為例,Method、路徑與部分 Header 可以用 HTTP/1.1 文字格式表示:

GET /account HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 ... Chrome/140.0 ...
Cookie: session=example

其中,Host 指定目標主機,User-Agent 描述 Client 宣告的軟體資訊,Cookie 則攜帶應用程式狀態。這些欄位都位於 HTTP 層。若實際使用 HTTP/2 或 HTTP/3,相同請求語意會透過 binary frame、pseudo-header 與 header compression 傳送,不會直接使用上面的文字格式。

HTTP 層的資料還需要由底層連線承載。以新建立的 HTTPS 連線為例,Client 需要取得目標位址,並完成傳輸連線與 TLS 握手所需的協商。HTTP/1.1、HTTP/2 常見的路徑是 TLS over TCP;HTTP/3 則使用 QUIC over UDP,將 TLS 1.3 握手整合進 QUIC。兩條路徑的分層關係如下:

HTTP/1.1 或 HTTP/2

Application behavior
        ↓
HTTP request、Header、Cookie
        ↓
HTTP/1.1 message 或 HTTP/2 frame/HPACK
        ↓
TLS over TCP
        ↓
IP 與 network path


HTTP/3

Application behavior
        ↓
HTTP/3 frame/QPACK
        ↓
TLS 1.3 handshake integrated with QUIC
        ↓
QUIC over UDP
        ↓
IP 與 network path

沿著這個分層關係,可以看出各種設定的作用位置:修改 User-Agent 影響的是 HTTP Header,TLS 與 HTTP protocol 的連線參數則由對應元件控制。各層留下的特徵因此可以分開觀察,再放回同一條連線中比對。

伺服器端可觀測資訊

從接收連線的一端觀察,每一層提供的資訊都不同。IP 與 TCP 反映網路路徑及作業系統特徵,TLS 與 HTTP protocol 則更接近 Client 使用的函式庫與設定。這些連線特徵通常稱為 Transport Fingerprint。

網站也可能透過 JavaScript 蒐集 Canvas、WebGL、字型與畫面尺寸,形成瀏覽器執行環境的指紋。這類資料可以和 Transport Fingerprint 一起參與風險評分,但需要另外蒐集,不能單靠 TLS 或 HTTP 封包取得。常見訊號的來源與限制可整理如下:

協定層 常見觀測值 主要影響來源 判讀限制
IP/Network Source IP、ASN、GeoIP、連線頻率 ISP、Proxy、VPN、NAT、Hosting Provider 多個使用者可能共用 IP,同一使用者也可能換 IP
TCP MSS、Window Scale、SACK、Timestamp、初始視窗 作業系統 Kernel、網路路徑、中介設備 一般 HTTP library 通常不直接產生 TCP SYN
TLS ClientHello、Cipher Suites、Extensions、ALPN TLS library、Client 設定、Session 狀態 JA3/JA4 只是部分欄位的摘要
HTTP/2 SETTINGS、Window Update、pseudo-header order、flow control HTTP/2 implementation 單一連線開頭不足以涵蓋後續 stream behavior
HTTP/3/QUIC QUIC version、Transport Parameters、HTTP/3 SETTINGS、QPACK QUIC 與 HTTP/3 implementation UDP、Proxy 與 fallback 會改變實際協商結果
HTTP Header 內容、順序、Cookie、request context Browser profile、Application code Header 可直接修改,不能單獨證明 Client 類型
Runtime/Behavior JavaScript API、DOM、navigation、timing、resource graph 真實瀏覽器環境與使用方式 不屬於 TLS/HTTP impersonation 的控制範圍

實際能蒐集到表中哪些資料,仍取決於觀測點。例如,CDN 終止訪客連線後,Origin 通常接收到的是 CDN 另外建立的連線。每筆 Fingerprint 都需要標明資料來自哪一段連線,才能合理比較。

瀏覽器與一般 HTTP Client 的實作差異

同樣遵循 HTTP 與 TLS 規範的 Client,仍會在這些欄位上留下差異。RFC 定義哪些訊息合法、欄位代表什麼,以及雙方如何協商;它通常不要求所有 Client 使用完全相同的參數組合與排列順序。各實作對這些選項的取捨,正是 Fingerprinting 的資料來源。

以 TLS 為例,Chromium network stack 使用 BoringSSL,Firefox 則使用 NSS。Python 或 Rust 程式可能透過 OpenSSL、rustls、平台原生 TLS,或綁定其他 C library 建立連線。各 TLS Stack 支援的功能、預設順序與版本更新時間不同。Chromium Network Stack 與 Mozilla Network Security Services 都有說明各自使用的安全元件。

HTTP Stack 也有相同現象。瀏覽器會根據頁面導覽、Fetch、圖片、字型或背景請求建立不同 Header 組合,並管理 connection reuse、stream multiplexing、priority 與 cache。一般 HTTP library 比較常從「送出一個 Request 並取得 Response」的 API 出發,即使 HTTP 語意相同,底層連線行為仍可能不同。

因此,比較瀏覽器與一般 HTTP Client 時,需要同時考慮 TLS Stack、HTTP Stack 與應用程式設定。Header 宣告的瀏覽器類型,是否和底層元件呈現的特徵相符,也能成為一項觀察結果。

TLS ClientHello 與 JA3/JA4

TLS Stack 的差異會直接反映在握手起點:ClientHello。Client 透過這個訊息提出自己支援的加密套件、版本與擴充能力,Server 再從中選擇可共同使用的設定。ClientHello 因而成為 TLS Fingerprint 最常使用的資料來源。

以 TLS 1.3 使用伺服器憑證驗證身分、未發生 HelloRetryRequest 的握手為例,ClientHello 與後續訊息的關係如下:

Client                                      Server
  │── ClientHello ───────────────────────────→│
  │←─ ServerHello ────────────────────────────│
  │←─ EncryptedExtensions                     │
  │←─ Certificate / CertificateVerify         │
  │←─ Finished                                │
  │── Finished ──────────────────────────────→│
  │                                           │
  │══════ Encrypted application data ════════│

TLS 1.3 將更多協商資訊放進 Extensions。依 RFC 8446,ClientHello 會表達支援版本、Cipher Suites,並依使用功能帶上 supported_groupskey_sharesignature_algorithmsserver_namepre_shared_key 等 Extensions。

ClientHello 欄位

Fingerprint 觀察的是 Client 提供了哪些能力,以及它如何組織這份能力清單。常見欄位與判讀方式如下:

欄位 功能 Fingerprint 觀察方式
legacy_version 保留舊版相容格式 TLS 1.3 仍使用 0x0303,不能直接當成最終版本
Cipher Suites Client 可接受的加密套件 ID 集合、數量與順序
Extensions SNI、ALPN、版本、群組等擴充能力 Extension ID 集合、數量、順序及內容
supported_versions Client 願意協商的 TLS versions 實際版本能力與偏好順序
supported_groups 可用的 ECDHE/DHE groups 群組集合與順序
key_share TLS 1.3 預先提供的 key exchange 資料 Group 選擇、數量與長度
signature_algorithms 可接受的簽章演算法 演算法集合與順序
ALPN 可用的 application protocol h2http/1.1h3 等協商能力
PSK/Session ticket Session resumption 與 0-RTT 相關能力 初次連線與續連線可能不同
Certificate Compression/ALPS 憑證壓縮與應用層設定傳遞 支援與否、演算法及 Extension 關係

TLS 1.3 的版本欄位特別容易被誤讀。ClientHello.legacy_version 必須填入 0x0303,也就是 TLS 1.2 的數值;真正提供的版本位於 supported_versions。因此,解析工具若只顯示最外層 version 771,不能直接下結論說 Client 只支援 TLS 1.2。

Session 狀態也會改變 ClientHello。初次連線、TLS resumption、PSK、0-RTT 與 HelloRetryRequest 可能產生不同的 Extension 組合。建立基準資料時,必須分開記錄 cold connection 與 resumed connection,否則同一套 Client 很可能被誤判為兩個 Profile。

RFC 8879 已定義 TLS Certificate Compression;ALPS 則是部分 Client 實作採用的 TLS Application-Layer Protocol Settings 機制。截至目前,ALPS 的 IETF Internet-Draft 已過期,沒有正式 RFC 地位。它仍可成為實作指紋,但不能因為真實瀏覽器曾採用,就把它描述成所有 TLS Client 都應支援的標準功能。

JA3

完整 ClientHello 適合保留作為原始證據,大量連線的索引與比對則需要較精簡的表示方式。JA3 採用的做法,是選取 ClientHello 中五組欄位,依固定格式串接成字串:

TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats

同一組內的 decimal ID 以 - 連接,五組之間使用 ,,移除 GREASE values 後再計算 MD5,得到便於索引的 32 字元 Hash。原始方法與參考實作可見 Salesforce JA3 repository;該 repository 已於 2025 年封存,現有產品仍可使用 JA3,但不應把封存的參考實作視為持續更新的 protocol standard。

JA3 的優點是格式簡單,既有 SIEM、IDS 與 log pipeline 容易保存及比對。它的限制也來自相同設計:

  • MD5 Hash 只適合當索引,不是身分簽章,也不具備密碼學身分證明。
  • 五組欄位沒有涵蓋 signature_algorithms、ALPN 內容、key_sharesupported_versions、PSK 與多項新 Extensions 的細節。
  • 原始 Extension 順序一變,Hash 就會改變。
  • TLS 1.3 的 legacy_version 可能讓 version 欄位看起來仍是 TLS 1.2。
  • 不同 Client 可以刻意或自然產生相同 JA3,同一 Client 也會因版本及 Session 狀態產生不同 JA3。

保留 JA3 Hash 卻丟棄原始字串及 ClientHello,日後只能比較摘要值,無法追查哪些原始欄位造成差異。

GREASE 與 Extension Permutation

JA3 的欄位中,有些值與順序會因 Client 的協定相容性設計而變動。GREASE 就是其中一種機制:RFC 8701 保留一組看似未知的 protocol values,讓 Client 定期送出這些值,藉此確認 Server 不會因遇到未知 Cipher、Extension、Group 或版本而失敗。它要避免的是 protocol ossification:生態系錯把目前已知值寫死,導致未來無法擴充。

Fingerprint calculator 通常會移除 GREASE values,再產生可比對的結果。否則同一個 Client 只因本次選到不同 GREASE value,就會得到沒有分析價值的新 Hash。

另一個變因是 Extension Permutation。Chromium 系 Client 已經採用 TLS Extension 順序變動,固定依賴原始順序的 JA3 會因此產生多個結果。順序仍然是封包事實,但它不再必然是穩定識別欄位。分析時應同時保留:

  • 原始 ClientHello,呈現這一次連線真正送出的順序。
  • 正規化結果,用來比對忽略已知隨機化後的 Client family。
  • 版本與執行環境,避免把軟體更新誤當成異常行為。

JA3N

針對 Extension Permutation 帶來的順序變動,常見的處理方式是先將 Extension IDs 排序,再產生摘要。JA3N 通常用來表示這類 normalized JA3,讓比較重點轉向忽略順序後的能力集合;原始 JA3 字串則保留所選欄位在這次握手中的排列。

JA3N 並非 IETF protocol,也不是原始 JA3 定義中的唯一標準格式。Diagnostic Service 若回傳 ja3n_hash,仍需確認它如何處理 GREASE、重複 Extension 與排序,才能和另一套工具的結果比較。

JA4

JA3N 主要處理既有欄位的順序問題,JA4 則重新選取、組織 TLS 特徵,將結果分成 a_b_c 三段。可讀的 a 段表達 TCP 或 QUIC、TLS version、SNI 狀態、Cipher 與 Extension 數量,以及 ALPN 的摘要;bc 段再分別摘要排序後的 Cipher 與 Extension/Signature Algorithm 特徵。這種分段方式可以只比對部分特徵,也降低 Extension permutation 對整體結果的影響。

FoxIO JA4 repository 提供目前的技術規格與實作。授權也要分開看:TLS Client Fingerprinting 的 JA4 採 BSD 3-Clause;JA4S、JA4H、JA4T 等其他 JA4+ 方法另有 FoxIO License。若要把它整合進商業產品,不能把整套 JA4+ 都視為與 JA4 相同授權。

格式 核心輸入 順序處理 適合用途 主要限制
JA3 TLS version、Cipher、Extension、Group、Point Format 保留 Cipher 與 Extension 順序 舊有資料比對、快速索引 新 TLS 特徵覆蓋不足,易受 permutation 影響
JA3N 常見實作以 JA3 為基礎正規化 Extensions 通常排序 Extension IDs Client family 聚合 各服務的正規化定義可能不同
JA4 Protocol、TLS、SNI、ALPN、Cipher、Extension、Signature Algorithm 等 對部分清單排序並分段摘要 跨 TCP/QUIC 分析、局部比對 仍是摘要,無法表示完整 handshake 與 runtime behavior

無論使用哪一種格式,Hash 都應和原始欄位、擷取時間、Client version、觀測位置一起保存。Hash 相同表示這個摘要值相同;考慮到正規化、未納入的欄位與碰撞可能性,仍需回到原始資料確認差異,也不能據此認定兩條連線來自同一個人、裝置或程式。

Encrypted ClientHello 的可見範圍

除了摘要方法,取得哪一份 ClientHello 也會影響結果。傳統 TLS Fingerprinting 經常假設 ClientHello 可由路徑上的觀測者直接讀取;啟用 Encrypted ClientHello(ECH)後,這個前提需要進一步區分。

2026 年 3 月發布的 RFC 9849 已將 ECH 標準化。ECH 將敏感的 ClientHelloInner 加密,網路中介者主要看到的是 ClientHelloOuter;持有 ECH key 並終止連線的伺服器,才能取得 Inner 內容。

ECH 不會讓連線完全失去可觀測特徵。IP、QUIC/TCP 行為、Outer ClientHello、封包長度與時間等訊號仍可能存在,但「ClientHello Fingerprint」必須進一步註明是根據 Outer、Inner,還是未使用 ECH 的傳統 ClientHello 計算。不同觀測點的 Hash 即使都標示 JA4,也未必代表輸入資料相同。

HTTP/2 與 HTTP/3 指紋

ClientHello 描述 Client 如何提出 TLS 協商能力,HTTP protocol 則決定請求在連線上如何傳送。以 HTTP/2 為例,Client 透過 ALPN 協商 h2 後,還要送出 connection preface 與 SETTINGS。即使兩個 Client 的 TLS 特徵相近,這些 HTTP/2 設定與後續 frame 行為仍可能不同。

HTTP/2 連線與 SETTINGS

HTTPS 上的 HTTP/2 建立流程可以簡化為:

TLS ClientHello(ALPN 提供 h2)
        ↓
TLS ServerHello/EncryptedExtensions(選擇 h2)
        ↓
HTTP/2 connection preface
        ↓
SETTINGS
        ↓
WINDOW_UPDATE、HEADERS、DATA ...

Client connection preface 以固定 24-byte sequence 開頭,後面緊接 SETTINGS frame。RFC 9113 規定雙方在連線開始時都要送出 SETTINGS,但 Client 不必明確送出每一項設定;省略的項目使用規範預設值。

HTTP/2 Setting ID 功能 可觀察差異
HEADER_TABLE_SIZE 0x01 HPACK dynamic table 上限 是否送出、設定值與排列位置
ENABLE_PUSH 0x02 Server Push 控制 Client 是否明確關閉
MAX_CONCURRENT_STREAMS 0x03 同時開啟 stream 上限 是否送出及數值
INITIAL_WINDOW_SIZE 0x04 每個 stream 初始 flow-control window 設定值與後續更新行為
MAX_FRAME_SIZE 0x05 接收 frame 的最大 payload 是否使用預設值
MAX_HEADER_LIST_SIZE 0x06 可接受 Header List 大小 Client 宣告值
ENABLE_CONNECT_PROTOCOL 0x08 Extended CONNECT 支援 WebSocket 等功能能力
NO_RFC7540_PRIORITIES 0x09 RFC 7540 priority scheme 使用方式 新舊 priority model 差異

SETTINGS 在 protocol 語意上是設定值,不是 Client ID;同一個 ID 重複出現時,後面的值取代前面的值。不過真實實作通常會以穩定順序及固定預設值建立 frame,伺服器便能把「有哪些設定、值是多少、依什麼順序出現」視為特徵。

SETTINGS 建立了連線初始參數,後續如何使用這條連線也會呈現實作差異,包含:

  • Connection-level WINDOW_UPDATE 的增量。
  • HEADERS frame 中 pseudo-header 的排列。
  • HPACK dynamic table 的使用方式。
  • Connection reuse、同時開啟的 streams 與 frame 拆分。
  • Flow control window 消耗與補充時機。
  • Request priority 與 stream lifecycle。

HTTP/2 規定 pseudo-header 必須出現在一般 Header 之前,但 :method:scheme:authority:path 的相對排列仍可能呈現實作慣例;CONNECT 等 Request 使用的 pseudo-header 組合也不同。這種順序可作為觀察值,不能當成 HTTP 語意上的身分欄位。

常見的 Akamai HTTP/2 Fingerprint String 將觀測值整理成下列四個區塊:

SETTINGS | WINDOW_UPDATE | PRIORITY | PSEUDO_HEADER_ORDER

這個摘要比只看 SETTINGS 完整,但仍未涵蓋 HPACK 狀態、後續 frame sequence、connection reuse 與 request timing。curl_cffi 的 TLS 與 HTTP/2 Fingerprint 文件 也採用 JA3 與 Akamai String 說明兩層 Profile 的差異。

HTTP/2 Priority

上述摘要中的 PRIORITY,需要連同 Client 採用的優先順序機制一起判讀。早期 HTTP/2 Fingerprint 常記錄 PRIORITY frame 與 stream dependency tree;RFC 9113 已將 RFC 7540 定義的 PRIORITY frame 標示為 deprecated,實作可改用 RFC 9218 的 extensible priority,包括 Priority Header 與 PRIORITY_UPDATE frame。

因此,「沒有 PRIORITY frame」不等於 Client 沒有 priority behavior。分析資料必須標明 Client 與 protocol implementation 的年代,並檢查 SETTINGS_NO_RFC7540_PRIORITIESPriority Header 及 PRIORITY_UPDATE。用舊版 Chrome 的固定 priority sequence 判斷現行 Client,很容易把正常版本演進當成異常。

HTTP/3、QUIC 與 QPACK

當連線使用 HTTP/3,觀察對象也要換成 QUIC 與 HTTP/3 自己的設定及 frame。依 RFC 9114,HTTP/3 建立在 QUIC 上,並在 TLS handshake 透過 ALPN token h3 選擇 protocol。QUIC 自身的 Transport Parameters 在連線建立階段協商;HTTP/3 專屬設定則由雙方在各自 control stream 的第一個 frame 送出 SETTINGS。

HTTP/3 可觀察的實作特徵包括:

  • QUIC version、Connection ID 長度與 Initial packet behavior。
  • QUIC Transport Parameters 的集合、值與排列。
  • TLS 1.3 ClientHello 與 ALPN h3
  • HTTP/3 control stream 建立順序及 SETTINGS。
  • QPACK table capacity、blocked streams 與 Header encoding 行為。
  • QUIC flow control、stream limit、Retry、connection migration 與 resumption。

HTTP/2 的 HPACK 依賴有序傳輸,無法直接套用在不同 QUIC streams 上。HTTP/3 因此使用 RFC 9204 定義的 QPACK,透過獨立 encoder/decoder streams 管理 dynamic table 與 blocking。能送出 HTTP/3 Request,只代表 protocol 可用;QPACK 與 control stream 的行為是否符合特定瀏覽器,仍是另一組問題。

實際連線可能因 UDP 被封鎖、Proxy 不支援 QUIC、Server 未提供 HTTP/3,或 Client 自身策略而改用 HTTP/2。Fingerprint log 必須記錄最後協商到的 protocol。程式要求「優先 HTTP/3」與封包實際使用 HTTP/3,是兩件不同的事。

Header、Browser Profile 與跨層一致性

TLS 與 HTTP/2、HTTP/3 提供了底層實作的觀察結果;回到開頭複製的 HTTP 請求,Header 則提供了 Client 宣告與這次請求的用途。把兩者放在一起,才能檢查應用層內容是否和連線特徵相符。

瀏覽器會依平台、隱私設定與請求情境組合 Header。除了 User-Agent,Client Hints、Fetch Metadata、內容協商與優先順序欄位,也各自表達不同資訊。

Header 內容與結構

類型 常見欄位 觀察重點
Client 宣告 User-Agentsec-ch-uasec-ch-ua-platform Browser family、版本與平台是否合理
Content negotiation AcceptAccept-EncodingAccept-Language 資源類型、壓縮能力、locale
Fetch context Sec-Fetch-SiteSec-Fetch-ModeSec-Fetch-Dest Navigation、same-origin、cross-site 與資源用途
Priority Priority urgency、incremental 與 protocol priority behavior
State Cookie、Authorization、cache validators Session 延續與 navigation history
Routing Host:authority Request 的 target authority

Header order 對多數不同名稱的欄位不是 HTTP application semantics,但實作仍常以固定資料結構和程式路徑產生穩定順序。HTTP/1.1 還可能觀察 field name casing;HTTP/2 與 HTTP/3 則要求 field names 使用 lowercase,不能把 H1 的 casing 規則直接套到 H2/H3。HTTP 欄位與訊息語意可參考 RFC 9110

除了欄位本身的格式,請求用途也會影響 Header 組合。同一個瀏覽器載入網頁、呼叫 API 或下載圖片時,會表達不同的內容需求與資源情境:

Request context Header Profile 差異
Top-level navigation Accept 偏向 HTML,Fetch Metadata 表達 navigation 與 document
Fetch/XHR AcceptContent-Type、CORS 與 Fetch Metadata 依 API 呼叫方式改變
Image/Font/CSS AcceptSec-Fetch-Dest 應反映資源類型
Form submission Method、Content-Type、Origin/Referer 與 navigation 狀態互相關聯
Service Worker/Cache Cache validators、request mode 與回應來源可能改變

把真實瀏覽器首頁導覽的 Header 原樣複製到每一個 API Request,不一定更像瀏覽器,反而可能形成不合理的 request context。

Browser Profile 一致性

當 TLS、HTTP protocol 與 Header 的觀察值放在一起,就可以整理出某個 Client 的特徵組合,通常稱為 Profile。比對 Browser Profile 時,除了各層是否接近已知瀏覽器,也需要檢查它們彼此是否合理,以及網路環境是否提供其他解釋:

觀察組合 技術判讀 其他合理解釋
User-Agent 宣告 Chromium,TLS/HTTP/2 也接近相同世代 Profile 跨層一致性較高 仍可能是模擬 Client
User-Agent 宣告瀏覽器,TLS 呈現通用 library 特徵 Profile 不一致 TLS intercepting proxy 可能重建 ClientHello
宣告支援 HTTP/3,但實際只建立 HTTP/2 不足以單獨判定異常 UDP、Server、Proxy 或網路政策可能阻擋 QUIC
Header 像 top-level navigation,URL 與 Fetch Metadata 卻像 background API Request context 不一致 Browser extension、Service Worker 或特殊導覽流程
TLS Hash 相同,但 IP、Cookie 與行為完全不同 可能是共用 Client implementation 同版瀏覽器自然會大量共用 Fingerprint

Fingerprint 的可靠性來自多項訊號共同支持,而不是把每個差異都當成惡意。企業 TLS inspection、VPN、Accessibility tool、Browser extension、舊設備與隱私功能都可能改變部分特徵。合理的偵測系統需要容許替代解釋,並以風險分數、觀察期與額外驗證處理不確定性。

Transport Fingerprint 的適用範圍

跨層一致性也說明了 Browser Impersonation 的目標:讓 HTTP Client 套用某個瀏覽器的 TLS、HTTP 設定與 Header 組合。不過,這些設定分屬不同元件,可模擬到的範圍取決於函式庫實際控制哪些部分。

HTTP Client 可控制的範圍

一般 HTTP library 與提供 Browser Impersonation 的 library,可以從各層的控制能力比較:

訊號 一般 HTTP library 具 Browser Impersonation 能力的 library 主要邊界
URL、Method、Header、Body 可控制 可控制 Application 必須建立正確 request context
Cookie/Session 可管理 可管理 不會自動產生真實 browsing history
TLS ClientHello 受 TLS backend 預設值限制 可套用或模擬特定 Profile Profile 版本、resumption 與 ECH 仍要一致
HTTP/2 SETTINGS/pseudo-header 通常只有部分設定 可調整較完整的 H2 Profile Runtime frame behavior 不一定完整相同
HTTP/3/QUIC 視 library 與 build 而定 視 Profile 與 QUIC stack 而定 UDP、Proxy 與 Server support 會改變結果
TCP SYN 主要由 Kernel 建立 通常仍由 Kernel 建立 TLS/HTTP Profile 不會自動改變 OS TCP Stack
Source IP/ASN 由網路出口決定 由網路出口決定 使用 Proxy 會引入新的信任與觀測邊界
JavaScript/DOM/Canvas 沒有 Browser runtime 沒有 Browser runtime 需要真實或可執行 Web API 的環境
Navigation/human behavior 由程式設計 由程式設計 Transport 模擬不會自動建立合理行為

curl_cffi 等工具可以調整 TLS 與 HTTP protocol Profile,但本質上仍不是含 DOM 與 JavaScript engine 的瀏覽器。其官方 Impersonation FAQ 也明確區分 TLS/HTTP impersonation 與完整 Browser environment。

TCP 指紋則是另一個常被忽略的邊界。一般 user-space HTTP library 呼叫作業系統 socket,由 Kernel 建立 TCP SYN;若沒有 raw socket、Kernel tuning 或特殊 network stack,僅修改 TLS Cipher 與 HTTP/2 SETTINGS 不會同步改變 MSS、Window Scale、SACK 與 Timestamp。這也是跨層分析可能看到「TLS 像瀏覽器、TCP 像執行程式的伺服器作業系統」的原因之一。

指紋證據的限制

既然函式庫可以重現部分瀏覽器特徵,觀察到相符的 Profile,就只能支持「這條連線具有相近的實作特徵」。同版瀏覽器也會讓大量使用者共享 Profile;軟體更新、A/B testing、企業 Proxy 與 Session resumption 則可能讓同一使用者產生差異。

Fingerprint 因而適合用於連線聚合、異常組合偵測,以及比較程式升級、TLS backend 或 Proxy 帶來的影響。這些結果可以協助 Bot management、abuse detection、threat hunting 與 incident response,但身分認證、存取授權與交易意圖仍需要應用程式自己的驗證。將 JA3 或 JA4 直接當成唯一封鎖條件,容易同時出現 false positive 與 false negative。

指紋觀察、驗證與判讀

實際比較瀏覽器與程式時,目標是找出差異來自哪個欄位、元件或網路條件。這需要把 Client 設定、實際連線與 Server 觀察結果對照起來:先確定觀測的是同一段連線,再控制實驗變因,最後解讀欄位與摘要的差異。

CDN、Proxy 與 TLS 終止點

選擇擷取位置時,需要先標出 TLS 在哪裡終止。以 CDN 終止訪客 TLS、再連往 Origin 的架構為例,兩段連線分別有自己的特徵:

Visitor/Client
      │ 連線 A:Client Fingerprint
      ▼
CDN/WAF/TLS Proxy
      │ 連線 B:CDN 或 Proxy Fingerprint
      ▼
Origin

CDN 終止 TLS 時,Edge 可觀察連線 A 的 ClientHello 與 HTTP behavior;Origin 直接看到的則是連線 B。除非 CDN 透過受信任欄位、metadata 或產品功能傳遞分析結果,Origin 不能從自己的 TLS log 還原 Client 原始 JA3。

Forward proxy 也要區分工作模式。HTTPS CONNECT tunnel 若不攔截 TLS,目標 Server 仍能看到 Client 建立的 TLS handshake,但 Source IP 通常是 Proxy;若 Proxy 執行 TLS inspection,Server 看到的 ClientHello 便是 Proxy 重新建立的連線。未記錄 Proxy mode 時,跨環境比較 Fingerprint 很容易得到錯誤結論。

加密與觀測位置

這些協定特徵雖然存在於連線中,擷取封包的位置卻未必能直接讀到。HTTPS 的 HTTP/2 SETTINGS、WINDOW_UPDATE、HEADERS 都位於 TLS encrypted application data 內;一般 on-path packet capture 無法只靠看到 TCP payload 就解析它們。HTTP/3 frames 同樣受到 QUIC encryption 保護。

能直接取得這些資料的位置通常是:

  • 終止 TLS/QUIC 的 Server、CDN 或 Reverse Proxy。
  • 支援 TLS key log 的 Client 搭配 packet capture。
  • HTTP library 自身的 debug/trace output。
  • 受控 Diagnostic Endpoint 對收到的連線進行 server-side parsing。

把「封包抓到了」直接寫成「HTTP/2 Fingerprint 已驗證」並不充分。若沒有 session secrets 或終端解析結果,capture 可能只證明建立了 TLS-over-TCP 連線,無法讀取加密後的 SETTINGS。

實驗紀錄基準

在確定觀測位置與可取得的資料後,可以依下列順序建立比對實驗:

  1. 在相同作業系統、網路出口與 Proxy 條件下建立基準連線。
  2. 一次只改一個變因,例如 Client library、TLS Profile 或 HTTP version。
  3. 同時保存 raw ClientHello、Server 解析欄位與 Hash。
  4. 分開測量新連線、connection reuse 與 session resumption。
  5. 重複多次,確認差異是穩定實作特徵或每次連線的隨機值。
  6. 對照 ALPN 與實際 HTTP protocol,不以 API 指定值代替 wire result。

為了讓各組結果能互相對照,每一組樣本至少記錄:

類型 必要資料
執行環境 作業系統、Kernel、CPU architecture、Container/VM
Client 軟體名稱、完整版本、HTTP library、TLS backend 與 build options
連線條件 URL、測試時間、DNS 結果、Proxy mode、Source IP、IPv4/IPv6
Protocol 實際協商的 TLS、ALPN、HTTP version、是否 resumption/ECH
原始證據 pcap/pcapng、Server log、Client trace、原始 ClientHello 欄位
摘要結果 JA3 string/Hash、JA3N 定義與 Hash、JA4、HTTP/2 Fingerprint

Browser Profile 名稱不能取代實際版本。chromechrome_latest 或「模擬最新版」會隨套件更新改變;可重現紀錄應固定 Client package version、Profile target 與測試日期。

Diagnostic Endpoint

有了固定的比較條件,可以先透過 Diagnostic Endpoint 查看 Server 解析到的 JA3、JA4、HTTP version 與 Header。它適合建立初步基準,但輸出仍受該服務的計算方式限制。相同名稱的 ja3n_hash 或 HTTP/2 Hash,不保證不同網站採用完全相同的正規化規則。

Packet Capture

Diagnostic Endpoint 提供的是 Server 的解析結果;若要進一步檢查原始 ClientHello 或 frame,則需要取得對應的封包。在 Ubuntu 上可安裝擷取與解析工具,並記錄版本:

sudo apt update
sudo apt install --yes tcpdump tshark

curl --version
tcpdump --version
tshark --version

若要解析同次測試的 HTTP/2 SETTINGS,應在建立測試連線前,依 Client 支援的方式啟用 TLS key logging,並保存對應的 key log。完成抓包後才啟用,無法補回先前握手所需的 session secrets。

在自有或明確授權的測試環境,先啟動下列指令擷取 TCP 443 與 UDP 443,再由 Client 建立測試連線:

sudo tcpdump \
  -i any \
  -s 0 \
  -w client-fingerprint.pcap \
  'tcp port 443 or udp port 443'

完成一組連線後按 Ctrl+C,再用 TShark 定位一般 TLS ClientHello:

tshark \
  -r client-fingerprint.pcap \
  -Y 'tls.handshake.type == 1' \
  -V

Wireshark GUI 可使用相同 display filter:

tls.handshake.type == 1

QUIC 與 HTTP/3 可先用下列 filter 定位:

quic || http3

Capture 檔可能包含 hostname、IP、Cookie、Authorization 與其他敏感資料,不應直接提交公開 repository。實驗保存時應限制權限、設定保留期限,公開範例則使用清理後的欄位摘要。

TLS Key Log 與 HTTP/2 解析

解析已擷取的 HTTP/2 流量時,需要對應連線的 session secrets,才能解密 TLS application data。應確認 Client 在測試時確實產生了 key log,再於 Wireshark 的 TLS protocol preferences 指向該檔案。不同 Client 的啟用方式不同,不能只設定環境變數就假設一定成功。

Session secrets 的敏感程度等同於對應測試連線的解密能力。Key log 不應用於正式流量,不應與 pcap 一起公開,也不能包含真實帳號、Token 或個人資料。

解密成功後,才適合用下列 display filter 尋找 HTTP/2 SETTINGS:

http2.type == 4

如果 Wireshark 仍只顯示 TLS Application Data,應先排查:

  • Key log 是否對應同一次 process 與同一條 connection。
  • Client 是否重用了較早建立的 connection。
  • Capture 是否從 handshake 開始前就已啟動。
  • 實際 ALPN 是否為 h2,而不是 http/1.1
  • 流量是否經過另一個 TLS termination point。

判讀層級

觀察結果改變時,先比較原始欄位差異,再解釋其技術意義。若原始封包的差異只來自 GREASE value 或 Extension permutation,代表的含義和 Cipher Suite、ALPN 或 HTTP/2 flow control 改變並不相同。

整理取得的 Endpoint 輸出、Server log、Client trace 或封包後,結論應對應實際蒐集到的證據。只有 TLS 摘要、已比較兩個協定層,或另外驗證了應用程式狀態,能支持的判斷各不相同:

判讀層級 可成立的結論 不足以成立的結論
單一 JA3/JA4 選定欄位與某個已知 Profile 相同或相近 Client 必定是該瀏覽器
TLS + HTTP/2 兩個 protocol layers 具有一致或矛盾特徵 已重現完整 Browser behavior
TLS + HTTP + Session Transport 與 application state 的一致性更高 已確認自然人身分
加入 JavaScript/Behavior 可進一步評估 runtime 與互動合理性 可取代 authentication 或 authorization
長期多訊號資料 可建立風險模型與異常基準 每一筆高風險結果都是惡意行為

工程上的安全決策應保留原因碼與可觀測資料,例如 rare_tls_profile 記錄罕見的 TLS 特徵組合、header_context_mismatch 記錄 Header 與請求情境不符、session_velocity 記錄 Session 活動頻率相關的判讀依據。具體原因能協助追查誤判,也方便在瀏覽器版本更新後重新校準規則。

結論

將瀏覽器的請求搬到程式時,URL、Header 與 Cookie 描述了應用層內容,TLS 與 HTTP Stack 則決定連線如何建立與傳送。HTTP Client Fingerprint 把這些實作特徵整理成可比較的資料;JA3、JA3N、JA4 與 Akamai HTTP/2 String 提供了不同範圍的摘要。

有意義的比較,需要在已知版本與網路條件下,將摘要差異追溯到原始欄位,並檢查各層是否一致。Browser Impersonation 能調整其中部分特徵,完整瀏覽器的執行環境與應用程式狀態則仍需分開驗證。這樣得到的結果,才能作為實作分析與風險判讀的依據。